package com.castle.fortress.admin.core.aspect;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.castle.fortress.admin.core.annotation.DataMasking;
import com.castle.fortress.admin.core.annotation.Dm;
import com.castle.fortress.admin.utils.DmUtil;
import com.castle.fortress.common.entity.RespBody;
import com.castle.fortress.common.enums.DataMaskingEnum;
import lombok.extern.log4j.Log4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 数据脱敏切面
 */
@Order(2)
@Aspect
@Component
@Log4j
public class DataMaskingAspect {
    @Pointcut("@annotation(com.castle.fortress.admin.core.annotation.DataMasking)")
    public void dataMaskingCut() {

    }

    @AfterReturning(value = "dataMaskingCut()",returning = "originalResult")
    public void dmAfter(JoinPoint joinPoint,Object originalResult){
        try {
            if(! (originalResult instanceof RespBody)){
                return ;
            }
            RespBody rb = (RespBody)originalResult;
            if(!rb.isSuccess()){
                return ;
            }
            // 获取注解
            MethodSignature signature =
                    (MethodSignature)joinPoint.getSignature();
            // 获取当前方法被Hh注解注释的注解对象
            DataMasking annotation =
                    signature.getMethod().getAnnotation(DataMasking.class);
            Dm[] dms = annotation.value();
            if(dms==null || dms.length < 1){
                return ;
            }
            rb.setData(replaceData(rb.getData(),dms));


        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 返回数据脱敏
     * @param originalData
     * @param dms
     * @return
     */
    private  Object replaceData(Object originalData,Dm[] dms){
        if(originalData instanceof IPage){
            return replaceOriginalData((IPage)originalData,dms);
        }else if(originalData instanceof List){
            return replaceOriginalData((List)originalData,dms);
        }else if(originalData instanceof Map){
            return replaceOriginalData((Map)originalData,dms);
        }else{
            return replaceOriginalData(originalData,dms);
        }
    }

    /**
     * 分页数据脱敏
     * @param original
     * @param dms
     * @return
     */
    private  Object replaceOriginalData(IPage original,Dm[] dms){
        original.setRecords(replaceOriginalData(original.getRecords(),dms));
        return original;
    }

    /**
     * 列表数据脱敏
     * @param original
     * @param dms
     * @return
     */
    private List replaceOriginalData(List original,Dm[] dms){
        List result = new ArrayList();
        for(Object obj:original){
          if(obj instanceof Map){
              result.add(replaceOriginalData((Map)obj,dms));
          }else{
              result.add(replaceOriginalData(obj,dms));
          }
        }
        return result;
    }

    /**
     * map数据脱敏
     * @param original
     * @param dms
     * @return
     */
    private  Object replaceOriginalData(Map original,Dm[] dms){
        for(Dm dm:dms){
            String key = dm.key();
            DataMaskingEnum dme = dm.type();
            if(original.containsKey(key)){
                original.put(key,replace(original.get(key),dme.getMetnodName()));
            }
        }
        return original;
    }

    /**
     * 实体类对象脱敏
     * @param original
     * @param dms
     * @return
     */
    private  Object replaceOriginalData(Object original,Dm[] dms){
        for(Dm dm:dms){
            String key = dm.key();
            DataMaskingEnum dme = dm.type();
            try {
                if(original.getClass().getDeclaredField(key)!=null){
                    String getMethodName = "get" + StrUtil.upperFirst(key);
                    String setMethodName = "set" + StrUtil.upperFirst(key);
                    Method getMethod = original.getClass().getMethod(getMethodName);
                    Method setMethod = original.getClass().getMethod(setMethodName,String.class);
                    Object data = getMethod.invoke(original);
                    setMethod.invoke(original,replace(data,dme.getMetnodName()));
                }
            } catch (Exception e) {
                log.error(e.getMessage());;
            }
        }
        return original;
    }

    public Object replace(Object s,String methodName){
        try {
            Method m =  DmUtil.class.getMethod(methodName,String.class);
            s = m.invoke(null,s);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return s;
    }
}

